#include "md-line-tokenizer.h"

#include <set>


namespace book
{

// 处理过程是有放回的，在某些预料之外的情况下，可能会出现错误放回导致死循环，设置一个最大重试次数
static const int32_t kMaxTryTimes = 1000;

MdToken MdLineTokenizer::next() {
    if (in_.hasNext()) {
        for (int32_t i = 0; i < kMaxTryTimes; ++i) {
            MdToken tok = matchToken(in_.next());
            if (tok) {
                return tok;
            }
        }
        throw std::logic_error("MdLineTokenizer may have some bug");
    } else {
        return MdToken();
    }
}

MdToken MdLineTokenizer::matchToken(char c) {
    if (MdToken flagToken = matchFlagToken(c)) {
        return flagToken;
    }

    if (c == '`') {
        return matchCodeToken();
    }

    return matchTextToken(c);
}

MdToken MdLineTokenizer::matchFlagToken(char c) {
    using Type = MdToken::Type;

    if (c == '*') {
        if (match("**")) {
            return MdToken(Type::StarBoldItalic);
        }
        if (match("*")) {
            return MdToken(Type::StarBold);
        }
        return MdToken(Type::StarItalic);
    }

    if (c == '_') {
        if (match("__")) {
            return MdToken(Type::UnderlineBoldItalic);
        }
        if (match("_")) {
            return MdToken(Type::UnderlineBold);
        }
        return MdToken(Type::UnderlineItalic);
    }

    if (c == '~') {
        if (match("~")) {
            return MdToken(Type::DeleteLine);
        }
        return MdToken(Type::Text, "~");
    }

    if (c == '<') {
        return MdToken(Type::UnnamedHrefLeftSide);
    }

    if (c == '>') {
        return MdToken(Type::UnnamedHrefRightSide);
    }

    return MdToken();
}

MdToken MdLineTokenizer::matchCodeToken() {
    std::string buffer;
    while (in_.hasNext()) {
        const char c = in_.next();
        if (c == '`') {
            break;
        }
        buffer.push_back(c);
    }
    return MdToken(MdToken::Type::Code, std::move(buffer));
}

MdToken MdLineTokenizer::matchTextToken(char c) {
    in_.restore(c);
    std::string text;
    while (in_.hasNext()) {
        const char c = in_.next();
        if (c == '*' || c == '_' || c == '~' || c == '`' || c == '<' || c == '>') {
            in_.restore(c);
            break;
        }
        if (c == '\\') {
            if (in_.hasNext()) {
                text.push_back(in_.next());
            } else {
                break;
            }
        } else {
            text.push_back(c);
        }
    }

    if (text.empty()) {
        return MdToken();
    } else {
        return MdToken(MdToken::Type::Text, std::move(text));
    }
}

bool MdLineTokenizer::match(const std::string_view &token) {
    std::string str;
    for (char tokenChar : token) {
        if (!in_.hasNext()) {
            in_.restore(str);
            return false;
        }
        const char c = in_.next();
        if (c != tokenChar) {
            in_.restore(c);
            in_.restore(str);
            return false;
        }
        str.push_back(c);
    }
    return true;
}



MdToken RestorableMdLineTokenizer::next() {
    if (!buffer_.empty()) {
        MdToken tok = std::move(buffer_.back());
        buffer_.pop_back();
        return tok;
    }
    return tokenizer_.next();
}

void RestorableMdLineTokenizer::pushFront(MdToken &&tok) {
    buffer_.push_back(std::move(tok));
}

} // namespace book
